package com.ljh.lottery.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ljh.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.ljh.lottery.common.exception.ServiceException;
import com.ljh.lottery.common.utils.BeanTransUtil;
import com.ljh.lottery.common.utils.JacksonUtil;
import com.ljh.lottery.common.utils.RedisUtil;
import com.ljh.lottery.mapper.*;
import com.ljh.lottery.pojo.dataobject.*;
import com.ljh.lottery.pojo.request.DrawPrizeRequest;
import com.ljh.lottery.pojo.request.WinningRecordsRequest;
import com.ljh.lottery.pojo.response.WinningRecordsResponse;
import com.ljh.lottery.service.DrawPrizeService;
import com.ljh.lottery.service.dto.WinningRecordsDTO;
import com.ljh.lottery.service.enums.ActivityPrizeStatusEnum;
import com.ljh.lottery.service.enums.ActivityStatusEnum;
import com.ljh.lottery.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import static com.ljh.lottery.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.ljh.lottery.common.config.DirectRabbitConfig.ROUTING;

/**
 * @author: Themberfue
 * @date: 2025/4/16 17:05
 * @description:
 */
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private static final Logger log = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";

    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Autowired
    RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeRequest param) {
        Map<String, String> message = Map.of(
                "messageId", String.valueOf(UUID.randomUUID()),
                "messageData", JacksonUtil.writeValueAsString(param)
        );
        // TODO 1. 发消息: 交换机，绑定的 KEY，消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, message);
        log.info("mq消息发送成功: map = {}", JacksonUtil.writeValueAsString(message));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeRequest param) {
        // TODO 1. 校验活动或奖品是否存在
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());

        QueryWrapper<ActivityPrizeDO> queryPrizeWrapper = new QueryWrapper<>();
        queryPrizeWrapper.lambda()
                .eq(ActivityPrizeDO::getActivityId, param.getActivityId())
                .eq(ActivityPrizeDO::getPrizeId, param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectOne(queryPrizeWrapper);

        QueryWrapper<ActivityUserDO> queryUserWrapper = new QueryWrapper<>();
        List<Long> userIds = param.getWinnerList()
                .stream()
                .map(DrawPrizeRequest.Winner::getUserId)
                .toList();
        queryUserWrapper.lambda()
                .eq(ActivityUserDO::getActivityId, param.getActivityId())
                .in(ActivityUserDO::getUserId, userIds);
        List<ActivityUserDO> activityUserDOS = activityUserMapper.selectList(queryUserWrapper);

        if (null == activityDO || null == activityPrizeDO || null == activityUserDOS) {
            log.error("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMsg());
            return false;
        }

        // TODO 2. 活动是否有效
        if (activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            log.error("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMsg());
            return false;
        }

        // TODO 3. 奖品是否有效
        if (activityPrizeDO.getStatus()
                .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            log.error("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getMsg());
            return false;
        }

        // TODO 4. 人员是否已被抽取
        for (ActivityUserDO activityUserDO: activityUserDOS) {
            if (activityUserDO.getStatus().equalsIgnoreCase(ActivityUserStatusEnum.COMPLETED.name())) {
                log.error("校验抽奖请求失败！失败原因：{}",
                        ServiceErrorCodeConstants.ACTIVITY_USER_COMPLETED.getMsg());
                return false;
            }
        }

        // TODO 5. 中奖者人数是否和设置奖品数量一致
        if (param.getWinnerList().size() != activityPrizeDO.getPrizeAmount()) {
            log.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR.getMsg());
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeRequest param) {
        // TODO 1. 查询活动、用户、奖品、活动奖品信息
        // TODO 1.1 查询活动信息
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        if (null == activityDO) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
        }

        // TODO 1.2 查询用户信息
        List<Long> userIds = param.getWinnerList()
                .stream()
                .map(DrawPrizeRequest.Winner::getUserId)
                .toList();
        List<UserDO> userDOS = userMapper.selectByIds(userIds);
        if (CollectionUtils.isEmpty(userDOS)) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_NULL);
        }

        // TODO 1.3 查询奖品信息
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        if (null == prizeDO) {
            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_INFO_IS_NULL);
        }

        // TODO 1.4 查询活动奖品信息
        QueryWrapper<ActivityPrizeDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ActivityPrizeDO::getActivityId, param.getActivityId())
                .eq(ActivityPrizeDO::getPrizeId, param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectOne(queryWrapper);
        if (null == activityPrizeDO) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }

        // TODO 2. 构造中奖记录数据，从中间人员列表维度构造
        List<WinningRecordDO> winningRecordDOS = userDOS.stream()
                .map(userDO -> BeanTransUtil.transToWinningRecordDO(activityDO, userDO,
                        prizeDO, activityPrizeDO,
                        param.getWinningTime()))
                .toList();

        // TODO 3. 保存中奖记录到数据库
        winningRecordMapper.insert(winningRecordDOS);

        // TODO 4. 将中奖记录缓存至 Redis
        // TODO 4.1 缓存奖品维度中奖记录 (WinningRecord_activityId_prizeId, winningRecordDOList（奖品维度的中奖名单）)
        cacheWinningRecords(activityDO.getId(), prizeDO.getId(),
                winningRecordDOS, WINNING_RECORDS_TIMEOUT);

        // TODO 4.2 缓存活动维度中奖记录(WinningRecord_activityId, winningRecordDOList(活动维度的中奖名单))
        // ! 当活动已完成再去存放活动维度中奖记录
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            // TODO 4.2.1 从数据库中查询本次活动全部的中奖记录
            QueryWrapper<WinningRecordDO> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.lambda()
                    .eq(WinningRecordDO::getActivityId, param.getActivityId());
            List<WinningRecordDO> allWinningRecordDOS = winningRecordMapper.selectList(queryWrapper1);
            cacheWinningRecords(param.getActivityId(), -1L, allWinningRecordDOS, WINNING_RECORDS_TIMEOUT);
        }

        return winningRecordDOS;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        // TODO 1. 校验参数
        if (null == activityId) {
            log.warn("要删除中奖记录相关的活动id为空！");
            return;
        }

        // TODO 2. 删除数据库中的数据
        QueryWrapper<WinningRecordDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("activity_id", activityId);
        if (prizeId != null) {
            queryWrapper.eq("prize_id", prizeId);
        }
        winningRecordMapper.delete(queryWrapper);

        // TODO 3. 删除缓存，从活动维度或者奖品维度删除
        if (null != prizeId) {
            // TODO 3.1 从奖品维度删除
            deleteWinningRecords(activityId + "_" + prizeId);
        }
        // TODO 3.2 从活动维度删除，无论如何都需要删除
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordsDTO> getWinningRecords(WinningRecordsRequest param) {
        // TODO 1. 查询缓存
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId();
        List<WinningRecordDO> winningRecordDOS = getWinningRecords(key);
        if (!CollectionUtils.isEmpty(winningRecordDOS)) {
            return winningRecordDOS.stream()
                    .map(BeanTransUtil::transToWinningRecordsDTO)
                    .toList();
        }

        // TODO 2. 缓存没有数据，查询数据库
        QueryWrapper<WinningRecordDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(WinningRecordDO::getActivityId, param.getActivityId());
        if (null != param.getPrizeId()) {
            queryWrapper.lambda()
                    .eq(WinningRecordDO::getPrizeId, param.getPrizeId());
        }
        winningRecordDOS = winningRecordMapper.selectList(queryWrapper);
        if (CollectionUtils.isEmpty(winningRecordDOS)) {
            throw new ServiceException(ServiceErrorCodeConstants.WINNING_RECORDS_IS_EMPTY);
        }

        // TODO 3. 缓存至 Redis
        cacheWinningRecords(param.getActivityId(), param.getPrizeId(), winningRecordDOS, WINNING_RECORDS_TIMEOUT);

        // TODO 4. 构造返回
        return winningRecordDOS.stream()
                .map(BeanTransUtil::transToWinningRecordsDTO)
                .toList();
    }

    /**
     * 删除活动维度或奖品维度中奖记录缓存
     * @param key 键
     */
    private void deleteWinningRecords(String key) {
        key = WINNING_RECORDS_PREFIX + key;
        try {
            // TODO 1. 校验是否存在 key
            if (redisUtil.exists(key)) {
                redisUtil.delete(key);
            }
        } catch (Exception e) {
            log.error("删除中奖记录缓存异常，key: {}", key);
        }
    }

    /**
     * 缓存奖品维度中奖记录
     * @param activityId 活动 id
     * @param prizeId 此次抽取的奖品 id
     * @param winningRecordDOS 中奖记录
     */
    private void cacheWinningRecords(Long activityId, Long prizeId, List<WinningRecordDO> winningRecordDOS, Long time) {
        // TODO 1. 校验参数
        if (null == activityId || null == prizeId
                || CollectionUtils.isEmpty(winningRecordDOS)) {
            log.warn("要缓存的内容为空！key: {}, value: {}",
                    activityId + "_" + prizeId, JacksonUtil.writeValueAsString(winningRecordDOS));
            return;
        }

        // TODO 2. 构造 key 和 value
        String key = WINNING_RECORDS_PREFIX + activityId
                + (prizeId.equals(-1L) ? "" : "_" + prizeId);
        String value = JacksonUtil.writeValueAsString(winningRecordDOS);
        try {
            // TODO 3. 存入 Redis
            redisUtil.set(key, value, time);
        } catch (Exception e) {
            log.error("缓存中奖记录异常！key:{}, value:{}", key, value);
        }
    }

    /**
     * 从缓存中获取中奖记录
     * @param key 键
     * @return 完整的中奖记录
     */
    private List<WinningRecordDO> getWinningRecords(String key) {
        key = WINNING_RECORDS_PREFIX + key;
        try {
            String value = redisUtil.get(key);
            if (!StringUtils.hasText(value)) {
                return List.of();
            }
            return JacksonUtil.readListValue(value, WinningRecordDO.class);
        } catch (Exception e) {
            log.error("从缓存中查询中奖记录异常！key:{}", WINNING_RECORDS_PREFIX + key);
            return List.of();
        }
    }
}
